﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using log4net;
using Newtonsoft.Json;
using YetAnotherChat.Common;

namespace YetAnotherChat.TcpConsole
{
    /// <summary>
    /// Put items into a queue and use a thread to pushing them to WebAPI in ChatUI project to save to DB
    /// log message to txt file to backup
    /// </summary>
    internal class MessageQueue
    {
        private ILog _messageLogger = LogManager.GetLogger("MemoryQueueStorageMessageLog");
        private ILog _errorLogger = LogManager.GetLogger(typeof(MessageQueue));
        private ConcurrentQueue<EntityBase> _internalQueue;
        private Thread _savingThread;
        private int _maxItems = 10;
        private HttpClient _apiClient;
        public CancellationTokenSource CancelToken = new CancellationTokenSource();


        public MessageQueue()
        {
            _internalQueue = new ConcurrentQueue<EntityBase>();
            _apiClient = new HttpClient();
            _savingThread = new Thread(SavingThreadStart);
            _savingThread.IsBackground = true;
            _savingThread.Start();
        }

        ~MessageQueue()
        {
            if (_savingThread != null && _savingThread.IsAlive)
                _savingThread.Abort();
            if (_apiClient != null)
            {
                _apiClient.Dispose();
            }
        }

        void SavingThreadStart()
        {
            SaveMessages();
        }

        void SaveMessages(List<EntityBase> inputList =null)
        {
            while (!CancelToken.Token.IsCancellationRequested)
            {
                if (_internalQueue.Count > 0 || inputList != null)
                {
                    List<EntityBase> list = inputList == null ? new List<EntityBase>() : inputList;
                    for (var i = 0; i < _maxItems && _internalQueue.Count > 0; i++)
                    {
                        EntityBase r = null;
                        if (_internalQueue.TryDequeue(out r))
                            list.Add(r);
                    }
                    var content = new StringContent(JsonConvert.SerializeObject(list), Encoding.UTF8, "application/json");
                    var task = _apiClient.PostAsync(ConfigurationManager.AppSettings["MessageWebApiAddress"], content);
                    if (!task.Result.IsSuccessStatusCode)
                    {
                        _errorLogger.Error("Error saving messages through WebAPI");
                        //try to save this list of message only once more!!!
                        if (inputList == null)
                            SaveMessages(list);
                    }
                    //have to break the loop immediately if this is
                    ///a recursive call from an error saving
                    if (inputList != null)
                        break;
                }
                else
                {
                    Thread.Sleep(20);
                }
            }
        }

        public void Add(EntityBase e)
        {
            //save to log file to quickly commit to somekind of persistence storage, how fast this is ?
            //will be quicker than DB because log thread always hold the file open to write ?
            _messageLogger.Info(e);
            _internalQueue.Enqueue(e);
        }
    }
}
